package com.gloaba.common.redis.aspect;

import com.gloaba.common.core.utils.StringUtils;
import com.gloaba.common.redis.annotation.RedissonDistributedLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class RedissonDistributedLockAspect {

    private final RedissonClient CLIENT;

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedissonDistributedLock distributedLock) throws Throwable {
        String lockName = this.getRedisKey(joinPoint, distributedLock);
        int leaseTime = distributedLock.leaseTime();
        String errorDesc = distributedLock.errorDesc();
        int waitTime = distributedLock.waitTime();
        TimeUnit timeUnit = distributedLock.timeUnit();

        Object ret;
        //获取锁
        RLock lock = CLIENT.getLock(lockName);

        try {
            boolean locked = lock.tryLock(waitTime, leaseTime, timeUnit);
            if (!locked) {
                log.error(String.format("redisson tryLock(%s) fail", lockName));
                throw new RuntimeException(errorDesc);
            }

            ret = joinPoint.proceed();
        } catch (Throwable var12) {
            log.error("redisson distributedLock  joinPoint.proceed error", var12);
            throw var12;
        } finally {
            if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        return ret;
    }


    /**
     *  获取加锁的key
     * @param joinPoint
     * @param distributedLock
     * @return
     */
    private String getRedisKey(ProceedingJoinPoint joinPoint, RedissonDistributedLock distributedLock) {
        String key = distributedLock.key();
        Object[] parameterValues = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = nameDiscoverer.getParameterNames(method);
        if (StringUtils.isEmpty(key)) {
            if (parameterNames != null && parameterNames.length > 0) {
                StringBuffer sb = new StringBuffer();
                int i = 0;

                for(int len = parameterNames.length; i < len; ++i) {
                    sb.append(parameterNames[i]).append(" = ").append(parameterValues[i]);
                }

                key = sb.toString();
            } else {
                key = "redissionLock";
            }

            return key;
        } else {
            SpelExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression(key);
            if (parameterNames != null && parameterNames.length != 0) {
                EvaluationContext evaluationContext = new StandardEvaluationContext();

                for(int i = 0; i < parameterNames.length; ++i) {
                    evaluationContext.setVariable(parameterNames[i], parameterValues[i]);
                }

                try {
                    Object expressionValue = expression.getValue(evaluationContext);
                    return expressionValue != null && !"".equals(expressionValue.toString()) ? expressionValue.toString() : key;
                } catch (Exception var13) {
                    return key;
                }
            } else {
                return key;
            }
        }
    }
}
